package com.tiger.cloud.common.redis.id.generator;

import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import com.tiger.cloud.common.redis.IRedisTemplate;

@Component
public class CodisUniqueNumberProducer implements UniqueNumberProducer{

    private Logger log = LoggerFactory.getLogger(UniqueNumberProducer.class);

    private IRedisTemplate redisTemplate;//codisClient

    private String cacheKey="UniqueNumber";//cache key in redis

    private IdGenConfig idGenConfig;
    /**
     * 缓存keys
     */
    private Queue<Long> queue;

    public CodisUniqueNumberProducer(IRedisTemplate redisTemplate, IdGenConfig idGenConfig) {
        this.idGenConfig = idGenConfig;
        this.redisTemplate = redisTemplate;
        if(idGenConfig.isCache()){
            queue = new ConcurrentLinkedQueue<>();
        }
    }

    /**
     * 获取下一唯一数字
     *
     * @return
     */
    @Override
    public long next() {
        long resp;
        if(!idGenConfig.isCache()){
            resp = incrAndCompare(1);
        }else{
            Long val = queue.poll();
            if(val==null){
                fullfillQueue(idGenConfig.getCacheSize());
                resp = next();
            }else {
                resp = val;
            }
        }
        return resp;
    }
    
    private void fullfillQueue(int size){
        log.info("缓存耗尽,重新加载...");
        synchronized (queue){
            CacheRange range = fetch(size);
            for(long i=range.getMin();i<range.getMax();i++){
                queue.add(i);
            }
        }
        log.info("缓存耗尽,重新加载完毕...");
    }

    /**
     * 按偏移量增加,并视情况reset
     * @param size
     * @return
     */
    private long incrAndCompare(int size){
        long val = redisTemplate.incrby(cacheKey, size+0l);
        if (val > 9000000000000000000L) {//如果即将接近Long.MaxValue,重置cache
            resetCache();
        }
        return val;
    }

    /**
     * 获取缓存
     *
     * @param size
     * @return
     */
    private CacheRange fetch(int size) {
        Long val = incrAndCompare(size);
        return new CacheRange(val - size, val);
    }

    /**
     * 重置缓存
     */
    private void resetCache() {
        String lockKey = cacheKey + ".lock";
        //可以增加个临时锁,避免集群环境下的并发问题(因为会涉及del)
        String val = redisTemplate.get(lockKey);
        //if (val != null && !"".equals(val)) {
        if(StringUtils.isNotBlank(val)){
            log.warn("其他进程/线程正在重置缓存中...本机不做处理...");
            return;
        }
        redisTemplate.set(lockKey, 60,"true");//60秒超时
        redisTemplate.del(cacheKey, lockKey);//reset key value
    }

}
